<?php
namespace bee;

class db implements Isingleton
{
  use bee\Tsingleton;
  private $__secConnections = array();
  private $__secConnParams = array();
  private $__connName;
  private $__CONN_STRING;
  private $__DB_ENGINE;
  private $__NAME_OPEN;
  private $__NAME_CLOSE;
  private $__PDO;
  private $__USERNAME;
  private $__PASSWORD;


  const DONT_FETCH                      = 0;
  const FETCH_FIRST_ROW                 = 1;
  const FETCH_FIRST_VALUE               = 2;
  const FETCH_ALL                       = 3;

  const ENGINE_MSSQL                    = 1;
  const ENGINE_MYSQL                    = 2;
  const ENGINE_ORACLE                   = 4;
  const ENGINE_ODBC                     = 32768; // trebalo bi ga koristiti samo u kombinacijia sa nekim db engineom
  const ENGINE_MSSQL_ODBC               = db::ENGINE_MSSQL | db::ENGINE_ODBC);


  protected function __construct($param = NULL)
  {
    if($name)
    {
      if(!isset($db->__secConnParams[$name]))
      {
        throw new exception("Connection with name $name not found.", 6969);
      }
      $db = db::getInstance();

      $this->__PDO = new \PDO($db->__secConnParams[$name]->CONN_STRING, $db->__secConnParams[$name]->username, $db->__secConnParams[$name]->password);

      $this->__PDO->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
      $this->__connName = $name;
    } else
    {
      $cfg = config::getInstance();
      $this->__PDO = new \PDO($cfg->sql__SERVER, $cfg->sql__USERNAME, $cfg->sql__PASSWORD); // , array("Database" => $cfg->sql__DB_NAME) );
                                                                                              // echo $cfg->sql__DB_NAME;
      $this->__PDO->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);

      if(($cfg->sql__DATABASE_TYPE & DB_ENGINE_MYSQL) == DB_ENGINE_MYSQL)
      {
        $this->__PDO->exec("SET NAMES 'UTF8'");
        $this->__NAME_OPEN = "`";
        $this->__NAME_CLOSE = "`";
      }
      if(($cfg->sql__DATABASE_TYPE & DB_ENGINE_MSSQL) == DB_ENGINE_MSSQL)
      {
        $this->__PDO->exec("SET NOCOUNT ON"); // ??????
        $this->__NAME_OPEN = "[";
        $this->__NAME_CLOSE = "]";
      }
      if(($cfg->sql__DATABASE_TYPE & DB_ENGINE_ORACLE) == DB_ENGINE_ORACLE)
      {
        $this->__NAME_OPEN = '"';
        $this->__NAME_CLOSE = '"';
      }
    }
  }

  /**
   * @return connectionParams
   */
  public function getConnectionParams()
  {
  	$cp = new connectionParams();
  	$cp->CONN_STRING          = $this->__CONN_STRING;
  	$cp->DB_ENGINE            = $this->__DB_ENGINE;
  	$cp->NAME_CLOSE           = $this->__NAME_CLOSE;
  	$cp->NAME_OPEN            = $this->__NAME_OPEN;
  	$cp->password             = $this->__PASSWORD;
  	$cp->username             = $this->__USERNAME;
  	return $cp;
  }


  public function addSecConnection($name, $connString, $dbUsername, $dbPassword, $dbEngine)
  {
    $this->__secConnParams[$name] = new connParams();
    $this->__secConnParams[$name]->CONN_STRING    = $connString;
    $this->__secConnParams[$name]->username       = $dbUsername;
    $this->__secConnParams[$name]->password       = $dbPassword;
    $this->__secConnParams[$name]->DB_ENGINE      = $dbEngine;

    if(($dbEngine & DB_ENGINE_MYSQL) == DB_ENGINE_MYSQL)
    {
      $this->__secConnParams[$name]->NAME_OPEN    = "`";
      $this->__secConnParams[$name]->NAME_CLOSE   = "`";
    }

    if(($dbEngine & DB_ENGINE_MSSQL) == DB_ENGINE_MSSQL)
    {
      $this->__secConnParams[$name]->NAME_OPEN    = "[";
      $this->__secConnParams[$name]->NAME_CLOSE   = "]";
    }

    if(($dbEngine & DB_ENGINE_ORACLE) == DB_ENGINE_ORACLE)
    { $this->__secConnParams[$name]->NAME_OPEN    = '"';
      $this->__secConnParams[$name]->NAME_CLOSE   = '"';
    }
  }

  public function getSecConnection($name)
  {
    if(isset($this->__secConnections[$name]))
    {
      return $this->__secConnections[$name];
    }

    if(!isset($this->__secConnParams[$name]))
    {
    	throw new exception("Connection named $name not found", 6969);
    }


    $this->__secConnections[$name] = new db($name);
    if(($this->__secConnParams[$name]->DB_ENGINE & DB_ENGINE_MYSQL) == DB_ENGINE_MYSQL)
    {
      $this->__secConnections[$name]->exec("SET NAMES 'UTF8'");
      $this->__secConnections[$name]->__NAME_OPEN    = "`";
      $this->__secConnections[$name]->__NAME_CLOSE   = "`";
    }

    if(($this->__secConnParams[$name]->DB_ENGINE & DB_ENGINE_MSSQL) == DB_ENGINE_MSSQL)
    {
      $this->__secConnections[$name]->exec("SET NOCOUNT ON");
      $this->__secConnections[$name]->__NAME_OPEN    = "[";
      $this->__secConnections[$name]->__NAME_CLOSE   = "]";
    }

    if(($this->__secConnParams[$name]->DB_ENGINE & DB_ENGINE_ORACLE) == DB_ENGINE_ORACLE)
    {
      $this->__secConnections[$name]->__NAME_OPEN    = '"';
      $this->__secConnections[$name]->__NAME_CLOSE   = '"';
    }

    return  $this->__secConnections[$name];
  }

  public function execSQL(afterWhere $sql, $fetch_mode = DB_FETCH_FIRST_VALUE, $pdo_fetch_style = \PDO::FETCH_BOTH)
  {
    $return = "";
    $stmt = $this->getInstance()->prepare($sql->text);
    if($stmt->execute($sql->getParams()))
    {
      if($fetch_mode          == DB_DONT_FETCH)
      {
        return true;
      } else if($fetch_mode   == DB_FETCH_FIRST_ROW)
      {
        $return = $stmt->fetch($pdo_fetch_style);
      } else if($fetch_mode   == DB_FETCH_FIRST_VALUE)
      {
        $row = $stmt->fetch(\PDO::FETCH_NUM);
        return $row[0];
      } else if($fetch_mode   == DB_FETCH_ALL)
      {
        $return = $stmt->fetchAll($pdo_fetch_style );
      }
    } else
    {
        throw new exception("Sql $sql execution failed", 6969);
    }

    return $return;
  }

  public function execSQLByDAORow(DAO $DAORow, afterWhere $sql = NULL, $fetch_mode = DB_FETCH_FIRST_VALUE, $pdo_fetch_style = \PDO::FETCH_BOTH)
  {
    $where_refl = new \ReflectionClass(get_class($DAORow));
    $cls_props = $where_refl->getProperties(\ReflectionProperty::IS_PUBLIC);
    $query_arr = array();
    $param_array = array();

    foreach($cls_props as $prop)
    {
      if(!empty($DAORow->{$prop->name}))
      {
        $query_arr[$prop->name] = $this->__NAME_OPEN . $prop->name . $this->__NAME_CLOSE . " = :" . $prop->name ;
        $param_array[":" .$prop->name] = $DAORow->{$prop->name};
      }
    }

    $sqt = $sql->getText() . (  $query_arr ? (" AND " . implode(" AND ", $query_arr)) : " ");
    $stmt = $this->getInstance()->prepare($sqt);

    if($stmt->execute(array_merge($sql->getParams(), $param_array)))
    {
      if($fetch_mode          == DB_DONT_FETCH)
      {
        return true;
      } else if($fetch_mode   == DB_FETCH_FIRST_ROW)
      {
        $return = $stmt->fetch($pdo_fetch_style);
      } else if($fetch_mode   == DB_FETCH_FIRST_VALUE)
      {
        $row = $stmt->fetch(\PDO::FETCH_NUM);
        return $row[0];
      } else if($fetch_mode   == DB_FETCH_ALL)
      {
        $return = $stmt->fetchAll($pdo_fetch_style);
      }
      } else
      {
        return false;
      }
      return $return;
    }
    public function cleanUp()
    {
      $this->__secConnections = array();
      $this->__class = NULL;
    }


}